//
// 闹钟模块
//   -> 管理OLED模块和时间温湿度显示
//

package modules

import (
	"time"
	"errors"
	"os"
	"image/png"
	"fmt"
	"adai.design/homemaster/log"
	"image"
)

const (
	// 字符在图片的位置
	oledLargeTitlePosY      = 0
	oledLargeTitleHeight    = 52
	oledTitlePosY      = 52
	oledTitleHeight    = 42
	oledSubtitlePosY   = 94
	oledSubtitleHeight = 24

	// 显示单位
	oledUnitPercentage = '%'
	oledUnitCelsius = 'c'

	// 屏幕现实的内容的位置
	oledTimePosY1 = 14
	oledTimePosY2 = 20
	oledSensorPosY1 = 78
	oledSensorPosY2 = 72
)


var oledLargeTitleCharInfoMap = map[byte]oledCharInfo{
	'0': {x: 1, y: oledLargeTitlePosY, width: 36, height: oledLargeTitleHeight},
	'1': {x: 38, y: oledLargeTitlePosY, width: 28, height: oledLargeTitleHeight},
	'2': {x: 66, y: oledLargeTitlePosY, width: 36, height: oledLargeTitleHeight},
	'3': {x: 102, y: oledLargeTitlePosY, width: 37, height: oledLargeTitleHeight},
	'4': {x: 139, y: oledLargeTitlePosY, width: 38, height: oledLargeTitleHeight},
	'5': {x: 177, y: oledLargeTitlePosY, width: 38, height: oledLargeTitleHeight},
	'6': {x: 215, y: oledLargeTitlePosY, width: 39, height: oledLargeTitleHeight},
	'7': {x: 254, y: oledLargeTitlePosY, width: 34, height: oledLargeTitleHeight},
	'8': {x: 288, y: oledLargeTitlePosY, width: 38, height: oledLargeTitleHeight},
	'9': {x: 326, y: oledLargeTitlePosY, width: 38, height: oledLargeTitleHeight},
	':': {x: 364, y: oledLargeTitlePosY, width: 16, height: oledLargeTitleHeight},
}

var oledTitleCharInfoMap = map[byte]oledCharInfo{
	'0': {x: 0, y: oledTitlePosY, width: 31, height: oledTitleHeight},
	'1': {x: 31, y: oledTitlePosY, width: 20, height: oledTitleHeight},
	'2': {x: 54, y: oledTitlePosY, width: 29, height: oledTitleHeight},
	'3': {x: 83, y: oledTitlePosY, width: 31, height: oledTitleHeight},
	'4': {x: 114, y: oledTitlePosY, width: 32, height: oledTitleHeight},
	'5': {x: 146, y: oledTitlePosY, width: 30, height: oledTitleHeight},
	'6': {x: 176, y: oledTitlePosY, width: 31, height: oledTitleHeight},
	'7': {x: 207, y: oledTitlePosY, width: 28, height: oledTitleHeight},
	'8': {x: 235, y: oledTitlePosY, width: 31, height: oledTitleHeight},
	'9': {x: 266, y: oledTitlePosY, width: 31, height: oledTitleHeight},
	':': {x: 297, y: oledTitlePosY, width: 13, height: oledTitleHeight},
}

var oledSubtitleCharInfoMap = map[byte]oledCharInfo{
	'0': {x: 0, y: oledSubtitlePosY, width: 18, height: oledSubtitleHeight},
	'1': {x: 18, y: oledSubtitlePosY, width: 13, height: oledSubtitleHeight},
	'2': {x: 31, y: oledSubtitlePosY, width: 17, height: oledSubtitleHeight},
	'3': {x: 48, y: oledSubtitlePosY, width: 18, height: oledSubtitleHeight},
	'4': {x: 66, y: oledSubtitlePosY, width: 18, height: oledSubtitleHeight},
	'5': {x: 84, y: oledSubtitlePosY, width: 18, height: oledSubtitleHeight},
	'6': {x: 102, y: oledSubtitlePosY, width: 18, height: oledSubtitleHeight},
	'7': {x: 120, y: oledSubtitlePosY, width: 16, height: oledSubtitleHeight},
	'8': {x: 136, y: oledSubtitlePosY, width: 18, height: oledSubtitleHeight},
	'9': {x: 154, y: oledSubtitlePosY, width: 18, height: oledSubtitleHeight},
	oledUnitPercentage: {x: 188, y: oledSubtitlePosY, width: 18, height: oledSubtitleHeight},
	oledUnitCelsius: {x: 172, y: oledSubtitlePosY, width: 16, height: oledSubtitleHeight},
	' ': {x: 206, y: oledSubtitlePosY, width: 10, height: oledSubtitleHeight},
}

type oledCharInfo struct {
	x int
	y int
	width int
	height int
}

var imageInstance = func() image.Image {
	f, err := os.Open(clockImageSrc)
	if err != nil {
		return nil
	}
	defer f.Close()

	img, err := png.Decode(f)
	if err != nil {
		return nil
	}
	return img
}()

func (t *oledCharInfo) getCode() ([]byte, error) {
	img := imageInstance
	if img == nil {
		return nil, errors.New("image source 404 not found")
	}
	if  t.x < 0 || t.y < 0 ||
		t.x > img.Bounds().Max.X || t.y > img.Bounds().Max.Y ||
		t.x + t.width > img.Bounds().Max.X || t.y + t.height > img.Bounds().Max.Y {
		return nil, errors.New("out of range")
	}

	buffer := make([]byte, t.width * t.height)
	for i := 0; i<t.height; i++ {
		for j := 0; j < t.width; j ++ {
			bit, _, _, _ := img.At(j + t.x, i + t.y).RGBA()
			buffer[i * t.width + j] = byte(bit&0xff * oledLightnessMax / 255)
		}
	}
	return buffer, nil
}

const (
	oledWidth = 128
	oledHeight = 128
	oledLightnessMax = 8
)


const (
	OledModeTime           = 1 // 正常显示时间和温湿度
	OledModeWifiConnecting = 2 // 正在连接wifi
	OledModeQRCodeApInfo   = 3 // AP热点信息
)


type clockScreen struct {
	mode 		int
	// 亮度
	buffer     [oledWidth][oledHeight]byte
	brightness uint8
	layout     chan bool

	// 显示内容
	time 		time.Time
	temperature int
	humidity 	int

	// wifi连接
	wifi 		string
	progress 	int

	// 显示位置
	titlePosY 	 int
	subTitlePosY int
}

func (s *clockScreen) drawn(x, y, w, h int, data []byte) error {
	if w <= 0 || h <= 0 || len(data) < w * h {
		return errors.New("invalid parameter")
	}

	for i:=0; i<h; i++ {
		for j:=0; j<w; j++ {
			if (i+y) > 0 && (i+y) < oledHeight &&
				(j+x) > 0 && (j+x) < oledWidth {
				s.buffer[i+y][j+x] = data[i*w+j]
			}
		}
	}
	return nil
}

func (s *clockScreen) encode() ([]byte, error) {
	for i:=0; i<oledHeight; i++ {
		for j:=0; j<oledWidth; j++ {
			s.buffer[i][j] = 0x00
		}
	}
	s.displayTime()
	s.displayHT()
	code := make([]byte, oledWidth * oledHeight / 2)
	for i:=0; i<oledHeight; i++ {
		for j:=0; j<oledWidth; j+=2 {
			code[(i*oledWidth + j)/2] = s.buffer[i][j] * s.brightness/ oledLightnessMax +
				((s.buffer[i][j+1] * s.brightness/ oledLightnessMax)&0x0F)<<4
		}
	}
	return code, nil
}

func (s *clockScreen) encodeWifi() ([]byte, error) {
	for i:=0; i<oledHeight; i++ {
		for j:=0; j<oledWidth; j++ {
			s.buffer[i][j] = 0x00
		}
	}

	s.displayWifiSignal()
	s.displayWifiName()
	code := make([]byte, oledWidth * oledHeight / 2)
	for i:=0; i<oledHeight; i++ {
		for j:=0; j<oledWidth; j+=2 {
			code[(i*oledWidth + j)/2] = s.buffer[i][j] * s.brightness/ oledLightnessMax +
				((s.buffer[i][j+1] * s.brightness/ oledLightnessMax)&0x0F)<<4
		}
	}
	return code, nil
}

func (s *clockScreen) encodeQRCode() ([]byte, error) {
	for i:=0; i<oledHeight; i++ {
		for j:=0; j<oledWidth; j++ {
			s.buffer[i][j] = 0x00
		}
	}

	s.displayQRCode()
	code := make([]byte, oledWidth * oledHeight / 2)
	for i:=0; i<oledHeight; i++ {
		for j:=0; j<oledWidth; j+=2 {
			code[(i*oledWidth + j)/2] = s.buffer[i][j] * s.brightness/ oledLightnessMax +
				((s.buffer[i][j+1] * s.brightness/ oledLightnessMax)&0x0F)<<4
		}
	}
	return code, nil
}

// 显示显示时间
func (s *clockScreen) displayTime() {
	var str string

	// 晚上0点显示晚上12:00
	if s.time.Hour() <= 12 && s.time.Hour() != 0 {
		str = s.time.Format("15:04")
	} else {
		str = s.time.Add(time.Hour * 12).Format("15:04")
	}

	var charMap map[byte]oledCharInfo
	bits := []byte(str)
	if bits[0] == '0' {
		bits = bits[1:]
		s.titlePosY = oledTimePosY1
		s.subTitlePosY = oledSensorPosY1
		charMap = oledLargeTitleCharInfoMap
	} else {
		s.titlePosY = oledTimePosY2
		s.subTitlePosY = oledSensorPosY2
		charMap = oledTitleCharInfoMap
	}

	var width = 0
	for _, c := range bits {
		if info, ok := charMap[c]; ok {
			width += info.width
		}
	}

	// 时间显示居中
	offset := (oledWidth - width) / 2
	for _, c := range bits {
		if info, ok := charMap[c]; ok {
			code, err := info.getCode()
			if err == nil {
				s.drawn(offset, s.titlePosY, info.width, info.height, code)
				offset += info.width
			}
		}
	}
}

var oledWifiLowerCase = []int{
	0, 12, 26, 39, 53, 65, 73,   // a b c d e f g
	87, 100, 105, 111, 123, 128, 148, // h i j k l m n
	161, 175, 189, 202, 211, 222, // o p q r s t
	230, 243, 255, 273, 285, 297, 310, // u v w x y z
}

var oledWifiSupperCase = []int {
	0, 16, 31, 48, 65, 79, 92,  // A B C D E F G
	110, 127,133,145,161,174,194, // H I J K L M N
	211,230,244,263,277,292, // O P Q R S T
	307,323,339,362,378,393,408, // U V W X Y Z
}

var oledWifiNumber = []int {
	0, 5, 12, 23, 39, 54, 72, 89, 95, 103, 111, 123, 138, 145, 155, 162,  //   !”#$%&’()*+,-./
	171, 186, 196, 211, 226, 241, 256, 271, 284, 299, //0123456789
	314, 320, 328, 342, 358, 372, 385, 407, // :;<=>?@
}

func (s *clockScreen) displayWifiName() {
	bits := []byte(s.wifi)

	var width = 0

	chars := make([]oledCharInfo, 0)
	for _, c := range bits {
		if c >= 'a' && c <= 'z' {
			char := oledCharInfo{
				x: oledWifiLowerCase[c - 'a'],
				y: 246,
				width: oledWifiLowerCase[c - 'a' + 1] - oledWifiLowerCase[c - 'a'],
				height: 30,
			}
			width += char.width
			chars = append(chars, char)
		} else if c >= 'A' && c <= 'Z' {
			char := oledCharInfo{
				x: oledWifiSupperCase[c - 'A'],
				y: 276,
				width: oledWifiSupperCase[c - 'A' + 1] - oledWifiSupperCase[c - 'A'],
				height: 29,
			}
			width += char.width
			chars = append(chars, char)
		} else if c >= ' ' && c <= '@' {
			char := oledCharInfo{
				x: oledWifiNumber[c - ' '],
				y: 305,
				width: oledWifiNumber[c - ' ' + 1] - oledWifiNumber[c - ' '],
				height: 29,
			}
			width += char.width
			chars = append(chars, char)
		} else if c == '_' {
			char := oledCharInfo{
				x: 411,
				y: 305,
				width: 423 - 411,
				height: 29,
			}
			width += char.width
			chars = append(chars, char)
		}

		if width > 110 {
			char := oledCharInfo{
				x: oledWifiNumber['.' - ' '],
				y: 305,
				width: oledWifiNumber['.' - ' ' + 1] - oledWifiNumber['.' - ' '],
				height: 29,
			}
			chars = append(chars[:len(chars)-3], char, char, char)
			break
		}
	}

	// 时间显示居中
	offset := (oledWidth - width) / 2
	for _, info := range chars {
		//log.Info("[%d] x: %d  y: %d  width: %d  height: %d", i, info.x, info.y, info.width, info.height)
		code, err := info.getCode()
		if err == nil {
			s.drawn(offset, 82, info.width, info.height, code)
			offset += info.width
		}
	}
}

func (s *clockScreen) displayWifiSignal() {
	offset := s.progress
	if offset > 4 {
		offset = 4
	}
	info := oledCharInfo{
		x:      offset * 128,
		y:      118,
		width:  128,
		height: 82,
	}
	code, err := info.getCode()
	if err == nil {
		s.drawn(0, 0, info.width, info.height, code)
	}
}

func (s *clockScreen) displayQRCode() {
	info := oledCharInfo{
		x:      4 * 128,
		y:      118,
		width:  128,
		height: 128,
	}
	code, err := info.getCode()
	if err == nil {
		s.drawn(0, 0, info.width, info.height, code)
	}
}

// 显示温湿度信息
func (s *clockScreen) displayHT() {
	str := fmt.Sprintf("%d%c %d%c", s.temperature, oledUnitCelsius, s.humidity, oledUnitPercentage)
	bits := []byte(str)

	var width = 0
	for _, c := range bits {
		if info, ok := oledSubtitleCharInfoMap[c]; ok {
			width += info.width
		}
	}

	// 居中
	offset := (oledWidth - width) / 2 + 4
	for _, c := range bits {
		if info, ok := oledSubtitleCharInfoMap[c]; ok {
			if code, err := info.getCode(); err == nil {
				s.drawn(offset, s.subTitlePosY, info.width, info.height, code)
				offset += info.width
			}
		}
	}
}

func (s *clockScreen) refresh() {
	var code []byte
	var err error
	if s.mode == OledModeTime {
		code, err = clock.encode()
	} else if s.mode == OledModeWifiConnecting {
		code, err = clock.encodeWifi()
	} else {
		code, err = clock.encodeQRCode()
	}

	if err != nil {
		log.Notice("clock encode err: %s", err)
	} else {
		msg := &MasterMessage{
			pkgType: MasterOled,
			data:    code,
		}
		err = masterSendMessage(msg)
		if err != nil {
			log.Notice("clock time display err: %s", err)
		}
	}
}

func (s *clockScreen) startDisplay() {
	s.time = time.Now().Add(time.Hour * 8)
	s.refresh()

	for {

		if s.mode == OledModeTime {
			duration := time.Duration(60 - time.Now().Second()) * time.Second
			select {
			case <- time.After(duration):
				clock.time = time.Now().Add(time.Hour * 8)
				//log.Info("time now: %s", clock.time.Format("2006/01/02 15:04:05"))
				// 时间属性改变
				clockService.Time.UpdateValue(clock.time.Format("15:04"))
				s.refresh()

			case _, ok := <- s.layout:
				if !ok {
					return
				}
				s.refresh()
			}
		} else if s.mode == OledModeWifiConnecting {
			select {
			case <- time.After(time.Millisecond * 500):
				for i:=0; i<4; i++ {
					s.progress = i
					s.refresh()
					time.Sleep(time.Millisecond * 500)
				}
				if s.mode == OledModeTime {
					time.Sleep(time.Millisecond * 500)
				}

			case _, ok := <- s.layout:
				if !ok {
					return
				}
				s.refresh()
			}

		} else if s.mode == OledModeQRCodeApInfo {
			select {
			case _, ok := <- s.layout:
				if !ok {
					return
				}
				s.brightness = 5
				s.refresh()
			}
		}
	}
}

var clock = &clockScreen{
	mode:       OledModeTime,
	wifi:       "#daodao",
	layout:     make(chan bool, 2),
	brightness: oledLightnessMax / 2,
}

func StartClock() {
	go clock.startDisplay()
}

func StopClock() {
	if clock.layout != nil {
		close(clock.layout)
		clock.layout = nil
	}
}

// 设置温湿度显示
func SetClockHT(t float32, h float32) {
	temperature := int(t + 0.5)
	humidity := int(h + 0.5)

	// 温湿度的显示值无变化，不刷新屏幕
	if temperature == clock.temperature && humidity == clock.humidity {
		return
	}

	clock.temperature = temperature
	clock.humidity = humidity
	if clock.layout != nil {
		clock.layout <- true
	}
}

// 设置自动亮度
func SetClockAutoBrightness(lux uint32) {
	var brightness uint8 = oledLightnessMax

	if lux < 45 {
		brightness = 4
	} else if lux < 150 {
		brightness = 4
	} else if lux < 300 {
		brightness = 5
	} else if lux < 500 {
		brightness = 6
	} else {
		brightness = 6
	}

	clock.time = time.Now().Add(time.Hour * 8)
	if clock.brightness == brightness {
		return
	}

	clock.brightness = brightness
	//clock.brightness = oledLightnessMax
	if clock.layout != nil {
		//log.Info("clock screen brightness level(%d) at %d lux", brightness, lux)
		clock.layout <- true
	}
}

func SetOledDisplay(data []byte) error {
	msg := &MasterMessage{
		pkgType: MasterOled,
		data:    data,
	}
	err := masterSendMessage(msg)
	if err != nil {
		log.Notice("clock time display err: %s", err)
		return err
	}
	return nil
}

func SetOledMode(mode int, wifi string) error {
	clock.mode = mode
	clock.wifi = wifi
	if clock.layout != nil {
		clock.layout <- true
	}
	return nil
}















